
#include<vector>
#include <errno.h>
#include <sys/iomsg.h>
#include <sys/iofunc.h>
#include <sys/dispatch.h>
#include <string.h>

#include "../m_connection_tests/komunikacja1.2.h"
#include "SamolotMessage.h"
#include "Samolot.h"

using namespace std;

Connector* Connector::instance = NULL;

static sem_t mutexConns;

Connector * Connector::getInstance()
{
	if ( !instance ) {
		instance = new Connector();
	}
	return instance;
}

Connector::Connector()
{
	connName = new char[CONN_NAME_LENGTH];
	connName[0] = 'a';
	for( int i = 0; i < CONN_NAME_LENGTH; i++)
		connName[i] = '\0';
	strcpy(connName, "NO_NAME");
	
	airportsNames = new std::vector<ConnNameForPlane>();
	connectionsOpened = new std::vector<ConnFD>();
	 
	sem_init( &mutexConns, 0, 1);
}

void Connector::createListener( char * _connLocalName)
{

	/** zapisuje nazwe naszego kanalu**/

	strcpy(connName, _connLocalName);

	/** watek, ktory bedzie nasluchiwal**/	
	pthread_attr_t attr;

   	pthread_attr_init( &attr );
   	pthread_attr_setdetachstate(
      		&attr, PTHREAD_CREATE_DETACHED );
      
	pthread_create( NULL, &attr, &run , _connLocalName );
}

char * Connector::getAirportName( int arg)
{
	int i;
	for(i = 0; i < airportsNames->size(); i++) {
		if( (*airportsNames)[i].airportId == arg)
			return  (*airportsNames)[i].airportName;
	}
	cout <<endl << "ERROR. Brak lotniska o tej nazwie" <<endl;
	return "NO_NAME";
	 
}

void Connector::showMeAirports( void)
{
	int i;
	for(i = 0; i < airportsNames->size(); i++) {
		cout << (*airportsNames)[i].airportName << " " << (*airportsNames)[i].airportId <<endl;
	}
	return;
	 
}

ConnNameForPlane::ConnNameForPlane( int a, char * name)
{
	airportId =  a;
	airportName = name;
	 
}

 int  Connector::isConnected( char * arg)
 {
 int i;

 	for(i = 0; i < connectionsOpened->size() ; i++) {
			if(  strcmp(     (* connectionsOpened)[i].connName , arg)== 0) {
			//	cout << "takie same : "<< arg << " i : " <<(* connectionsOpened)[i].connName <<endl;
				return  (* connectionsOpened)[i].fd;
			}
	}
		
	return -1;
 }

ConnFD::ConnFD(  char * name, int _fd)
{
	fd=  _fd;
	connName = name;
}

void  Connector::addConn( char * arg, int fd)
{
	char * par = new char[30];
	strcpy(par, arg);
	
	//cout << "addsConn : " << arg<<endl;
	connectionsOpened->push_back( *(new ConnFD( par, fd)));		
}

void  Connector::closeAllConns( void )
{
	
	int i, j;
	vector <ConnFD>::iterator iter;
	
	sem_wait(&mutexConns);
	
	iter = connectionsOpened->begin();
	i = 	connectionsOpened->size();
	for( j = 0; j < i; j++) {
		name_close( (*iter).fd);
		connectionsOpened->erase(iter);
		iter = connectionsOpened->begin();
	}
//	cout << "closeAllConns . polaczen jest  : "<< 	connectionsOpened->size() << endl;
	
	sem_post(&mutexConns);
}

 
/*** Client Side of the code ***/
int sendMessage(  Message arg) {

    my_data_t * msg = &arg.data;
    int fd;
	
	sem_wait(&mutexConns);
	
	if( (fd = Connector::getInstance()->isConnected(arg.getConnReceiverName()))==-1 ) {
    	if ((fd = name_open (arg.getConnReceiverName(), NAME_FLAG_ATTACH_GLOBAL)) == -1) {
    		cout <<"ERROR sendMessage. pewnie nie ma " <<arg.getConnReceiverName() <<" albo gns -c zapomniales"<<endl;
    	 	cout <<endl <<strerror(errno) << endl ;
        	sem_post(&mutexConns);
        	return EXIT_FAILURE;
    	}
    	Connector::getInstance()->addConn(arg.getConnReceiverName(), fd);
    }

    /* We would have pre-defined data to stuff here */
    (*msg).hdr.type = 0x00;
    (*msg).hdr.subtype = 0x00;

     if (MsgSend(fd, &(*msg), sizeof(*msg), NULL, 0) == -1) {
  		cout <<"\nsendMessage ERROR1 destination\n";
    	cout <<endl <<strerror(errno) << endl ;
    	sem_post(&mutexConns);
        return EXIT_FAILURE;
    }
    
    if( (fd = Connector::getInstance()->isConnected("GOOFY"))==-1 ) {
		if ((fd = name_open ( "GOOFY", NAME_FLAG_ATTACH_GLOBAL)) == -1) {
	  		cout <<"ERROR sendMessage. pewnie nie ma goofy albo gns -c zapomniales" <<endl;
	   		cout<< endl<<strerror(errno)<<endl;
	   		sem_post(&mutexConns);
        	return EXIT_FAILURE;
    	}
    	Connector::getInstance()->addConn("GOOFY", fd);
   	}
   
    /* We would have pre-defined data to stuff here */
    (*msg).hdr.type = 0x00;
    (*msg).hdr.subtype = 0x00;

  
    if (MsgSend(fd, &(*msg), sizeof(*msg), NULL, 0) == -1) {
    	cout <<endl<<"ERROR. cos nie tak z goofy"<<endl;
      	cout<< endl<<strerror(errno)<<endl;
      	sem_post(&mutexConns);
        return EXIT_FAILURE;
    }
	sem_post(&mutexConns);
    return EXIT_SUCCESS;
}






/**
	Funkcja zajmujaca sie komunikatem, ktory przyszedl przez siec
**/
void * messageReceived( void * arg)
{
	Message * msg = (Message *)arg;
	
	SamolotMessage sammsg;
	position pos;	
	int i;
	char * nazwa;
	std::vector<ConnNameForPlane> * airportsNames;
	
	switch( msg->getType()) {
		case MSG_LAP_LICZBE_LOTNISK :	
			sammsg.setReceiveMsgType( MSG_LAP_LICZBE_LOTNISK);
			sammsg.setLiczbaLotnisk(msg->getIntArg1());
			Samolot::getInstance().pickMessage(sammsg);
			break;
			
		case MSG_LAP_WSPOLRZEDNE_LOTNISKA :	
			sammsg.setReceiveMsgType( MSG_LAP_WSPOLRZEDNE_LOTNISKA);
			sammsg.setIdHelper(msg->getIntArg1());
			sammsg.setIdWiezy(msg->getIntArg1());
			sammsg.setNazwaLotniska( std::string(msg->getStringArg1()));
			pos.first = msg->getFloatArg1();
			pos.second = msg->getFloatArg2();
			sammsg.setPosition(pos);
			
			airportsNames =  Connector::getInstance()->airportsNames;
			for(i = 0; i < airportsNames->size() ; i++) {
				if( ((*airportsNames)[i]).airportId == msg->getIntArg1()) {
					i = 100;
					break;
				}
			}
			if( i != 100) {
				airportsNames->push_back( *(new ConnNameForPlane( msg->getIntArg1(),  msg->getStringArg1() )));
			}

		/*			
			if( msg->getNr() == msg->getMessageLength()) 
			{
				cout << "wyswietla liste"<<endl;
				 Connector::getInstance()->showMeAirports();
			}
	*/
			Samolot::getInstance().pickMessage(sammsg);
			break;
			
		case MSG_RES_ZAPODAJ_DANE_TECHNICZNE :	
			sammsg.setReceiveMsgType(  MSG_RES_ZAPODAJ_DANE_TECHNICZNE);
			sammsg.setFuelLvl(msg->getFloatArg1());
			sammsg.setPredkosc(msg->getFloatArg3());
			sammsg.setSpalanie(msg->getFloatArg2());
			Samolot::getInstance().pickMessage(sammsg);
			break;
		
		case MSG_ZEZWOLENIE_NA_START :	
			sammsg.setReceiveMsgType( MSG_ZEZWOLENIE_NA_START);
			sammsg.setCzasOczekiwania(0);
			Samolot::getInstance().pickMessage(sammsg);
			break;
			
		case FAILURE_EV_PL :	
			sammsg.setReceiveMsgType(MSG_SIE_ZESRRRALES);
			sammsg.setNazwaAwarii(msg->getStringArg1());
			sammsg.setTypAwarii( atoi(msg->getStringArg2()));
			sammsg.setMocAwarii(msg->getIntArg1());
			Samolot::getInstance().pickMessage(sammsg);
			break;
			
		case FAILURE_OVER_EV_PL :	
			sammsg.setReceiveMsgType( MSG_SIE_ODESRALES);
			sammsg.setIdHelper(msg->getIntArg1());
			sammsg.setNazwaAwarii(msg->getStringArg1());
			Samolot::getInstance().pickMessage(sammsg);
			break;
			
		case MSG_ZMIEN_KURS :	
			sammsg.setReceiveMsgType(  MSG_ZMIEN_KURS);
			pos.first = msg->getFloatArg1();
			pos.second = msg->getFloatArg2();
			sammsg.setPosition(pos);
			nazwa = new char[CONN_NAME_LENGTH];
			strcpy( nazwa, msg->getStringArg1() );
			sammsg.setNazwaLotniska( nazwa);
			Samolot::getInstance().pickMessage(sammsg);
			break;
			
		case MSG_MOZESZ_LADOWAC :	
			sammsg.setReceiveMsgType(MSG_MOZESZ_LADOWAC);
			sammsg.setCzasOczekiwania(0 );
			Samolot::getInstance().pickMessage(sammsg);
			break;
			
		case MSG_GON_SIE :	
			sammsg.setReceiveMsgType(MSG_GON_SIE);
			Samolot::getInstance().pickMessage(sammsg);
			break;

		case	ZAKAZ_STARTU :
			sammsg.setReceiveMsgType( MSG_ZEZWOLENIE_NA_START);
		//	cout << "start po czasie :  " <<  msg->getIntArg1()<<endl;
			sammsg.setCzasOczekiwania( msg->getIntArg1() );
			Samolot::getInstance().pickMessage(sammsg);
			break;
			
		case	ZAKAZ_LADOWANIA :
			sammsg.setReceiveMsgType( MSG_MOZESZ_LADOWAC);

			cout << "ladowanie po czasie :  " <<  msg->getIntArg1()<<endl;

			sammsg.setCzasOczekiwania( msg->getIntArg1() );
			Samolot::getInstance().pickMessage(sammsg);
			break;
			
			

		default : 
			break;
		}	
}


void messageHandler( Message  * msg)
{
//	cout << "messageHandler" <<endl;

	/* watek, ktory bedzie nasluchiwal*/
	pthread_attr_t attr;

   	pthread_attr_init( &attr );
   	pthread_attr_setdetachstate(
      		&attr, PTHREAD_CREATE_DETACHED );
      		
	pthread_create( NULL, &attr, &messageReceived, msg );
}



/**
	Funkcja obslugujaca nasluchiwanie na zadania z sieci
**/
void* run( void*  arg) {

   name_attach_t *attach;

   Message msg;
    Message * msgProper;
   int rcvid;
	
   /* Create a local name (/dev/name/local/...) */
   if ((attach = name_attach(NULL, (char *)  arg, NAME_FLAG_ATTACH_GLOBAL)) == NULL) {
   	cout << "ERROR,  run: #"<< strerror(errno) << endl;
       return (void*)EXIT_FAILURE;
   }

   /* Do your MsgReceive's here now with the chid */
   while (1) {
  		//cout <<"czekam..."<<endl;
       rcvid = MsgReceive(attach->chid, &(msg.data), sizeof(msg.data), NULL);


		
       if (rcvid == -1) {/* Error condition, exit */
           break;
       }

       if (rcvid == 0) {/* Pulse received */
           switch (msg.data.hdr.code) {
           case _PULSE_CODE_DISCONNECT:
               /*
                * A client disconnected all its connections (called
                * name_close() for each name_open() of our name) or
                * terminated
                */
                 Connector::getInstance()->closeAllConns();   
               ConnectDetach(msg.data.hdr.scoid);
               break;
           case _PULSE_CODE_UNBLOCK:
               /*
                * REPLY blocked client wants to unblock (was hit by
                * a signal or timed out).  It's up to you if you
                * reply now or later.
                */
               break;
           
           }
           continue;
       }


       /* name_open() sends a connect message, must EOK this */
       if (msg.data.hdr.type == _IO_CONNECT ) {
           MsgReply( rcvid, EOK, NULL, 0 );
           continue;
       }

       /* Some other QNX IO message was received; reject it */
       if (msg.data.hdr.type > _IO_BASE && msg.data.hdr.type <= _IO_MAX ) {
           MsgError( rcvid, ENOSYS );
           continue;
       }

       /* A message (presumable ours) received, handle */

     	MsgReply(rcvid, EOK, 0, 0);
     
  		msgProper = new Message();
		msg.copyMessage(msgProper);    
   
   		messageHandler(msgProper);
		
   } 

   /* Remove the name from the space */
   name_detach(attach, 0);


   return (void*) 0;
}

